package com.gzx.spring.drools.service;

import com.google.common.collect.Lists;
import com.gzx.risk.core.apis.command.ApiCommand;
import com.gzx.risk.core.apis.context.ApiContext;
import com.gzx.risk.core.apis.result.RiskResult;
import com.gzx.risk.core.pojo.CreditAuthInfo;
import com.gzx.risk.core.service.CreditService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.kie.api.runtime.KieSession;
import org.kie.internal.builder.conf.GroupDRLsInKieBasesByFolderOption;
import org.kie.internal.utils.KieHelper;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class DroolsService {
	static {
		System.setProperty(GroupDRLsInKieBasesByFolderOption.PROPERTY_NAME, "true");
	}
	@Resource
	private RiskLogService riskLogService;
    @Resource
    private CreditService creditService;

	private final ApplicationContext applicationContext;

	private static String loadRule(String name) throws Exception {
		return IOUtils.toString(DroolsService.class.getResourceAsStream(name), "UTF-8");
	}

	public DroolsService(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	public <T> Map<String, T> getImplementations(Class<T> interfaceClass) {
		return applicationContext.getBeansOfType(interfaceClass);
	}

	// riskLogService.log(rule + ".drl", ctx);
	public boolean exec(ApiContext ctx) throws Exception {
		boolean result = execReal(ctx);
		riskLogService.log(ctx);
        CreditAuthInfo authInfo = ctx.getUserInfo().getCreditAuthInfo();
        creditService.saveAuthInfo(authInfo);
		return result;
	}

    public boolean execRiskTwo(ApiContext ctx) throws Exception {
        boolean result = execCredit(ctx);
        if (ctx.getUserInfo().getIsB0013()){
            return result;
        }
        riskLogService.log(ctx);
        CreditAuthInfo authInfo = ctx.getUserInfo().getCreditAuthInfo();
        creditService.saveAuthInfo(authInfo);
        return result;
    }

    // 执行风控规则1
	private boolean execReal(ApiContext ctx) throws Exception {
		// 主规则
		if (!execApiRules(0, ctx)) {
			return false;
		}

		// 手机状态api--->规则
		if (!execApiRules(1, ctx)) {
			return false;
		}

		// 手机在网时长api--->规则
		if (!execApiRules(2, ctx)) {
			return false;
		}

		// yuyao 百融-特殊名单验证api(gatewayC108143)--->规则
		if (!execApiRules(3, ctx)) {
			return false;
		}

		// yuyao 百融-借贷意向验证3.0api(brdt_score)--->规则
		if (!execApiRules(4, ctx)) {
			return false;
		}

		// jishu 阿里云申反欺诈V5(edb003_v8)--->规则
		if (!execApiRules(5, ctx)) {
			return false;
		}

		// jishu 哈啰火眼分(ed006_v1)--->规则
		if (!execApiRules(6, ctx)) {
			return false;
		}

		// yixinsou 信易搜明鉴分95(score95)--->规则
		if (!execApiRules(7, ctx)) {
			return false;
		}

		// yuyao 司法个人(personal)--->规则
		if (!execApiRules(8, ctx)) {
			return false;
		}

		// yuyao 阿里云申反欺诈V5(gatewayC108139)--->规则
		if (!execApiRules(9, ctx)) {
			return false;
		}

		// xiaoniu 腾云分239(r301)--->规则
//		if (!execApiRules(10, ctx)) {,待升级
//			return false;
//		}

		// yuyao 融安云网分(ra_high_score)--->规则
		if (!execApiRules(11, ctx)) {
			return false;
		}

		// xiaoniu 腾云分212(r201)--->规则
		if (!execApiRules(12, ctx)) {
			return false;
		}

		// yixinsou 信易搜明鉴分95s(score95s)--->规则
		if (!execApiRules(13, ctx)) {
			return false;
		}

		// yuyao 天创信用司南(gatewayC108144)--->规则
		if (!execApiRules(14, ctx)) {
			return false;
		}

        // tanzhi 探知(探预分)--->规则
        if (!execApiRules(15, ctx)) {
            return false;
        }

        // tanzhi 探知(全域风险指数v2)--->规则
        if (!execApiRules(17, ctx)) {
            return false;
        }

        // 无提交授权成功
        if (StringUtils.isBlank(ctx.getUserInfo().getCreditAuthInfo().getSerialNumber())){
            if (!execApiRules(97, ctx)) {
                return false;
            }
        }

		// 期数计算
		this.execRules(ctx, "last");
		return ctx.getRuleResult();
	}

    // 执行风控规则2 异步的征信规则 (C9005 R9003)
    private boolean execCredit(ApiContext ctx) throws Exception {
        // 华融征信 C9005
        if (!execApiRules(98, ctx)) {
            return false;
        }

        // 华融征信 R9003
        if (!execApiRules(99, ctx)) {
            return false;
        }

        return ctx.getRuleResult();
    }

	private List<ApiCommand> findServices(int groupId) {
		Map<String, ApiCommand> services = getImplementations(ApiCommand.class);
		List<ApiCommand> apis = Lists.newArrayList();
		services.values().forEach(svc -> {
			if (svc.getGroupId() == groupId) {
				apis.add(svc);
			}
		});
		return apis;
	}

	private boolean execApiRules(int groupId, ApiContext ctx) throws Exception {
		String groupName = "other" + groupId;
		findServices(groupId).forEach((svc) -> {
			long start = System.currentTimeMillis();
			RiskResult riskResult = svc.exec(groupName, ctx);
			long used = System.currentTimeMillis() - start;
			if (riskResult != null) {
				log.info("groupId:{},apiName:{},used:{},success", groupId, svc.getClass().getSimpleName(), used);
				riskResult.setApiUsed(used);
			} else {
				log.info("groupId:{},apiName:{},used:{},fail", groupId, svc.getClass().getSimpleName(), used);
			}
		});

		this.execRules(ctx, groupName);
		return ctx.getRuleResult();
	}

	private void execRules(ApiContext ctx, String groupName) throws Exception {
		long start = System.currentTimeMillis();
		KieHelper helper = new KieHelper();
		helper.addContent(loadRule("/rules/risk/" + groupName + ".drl"), "/rules/risk/" + groupName + ".drl");
		KieSession ksession = helper.build().newKieSession();
		ksession.insert(ctx);
		for (Object result : ctx.getResults()) {
			ksession.insert(result);
		}

		ksession.fireAllRules();
		if (!ctx.getRuleResult()) {
			ctx.setGroupName(groupName);
		}

		ksession.dispose();
		long used = System.currentTimeMillis() - start;
		log.info("execRules groupName:{},used:{}", groupName, used);
	}

    public Boolean testExecRules(Integer groupId, ApiContext ctx) throws Exception {
        if (!execApiRules(groupId, ctx)) {
            return false;
        }
        return ctx.getRuleResult();
    }
}
